#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <set>

#include "dblib.h"

std::set<std::string> split(const std::string &s, const char &idle) 
{ 
    std::set<std::string> ret; 
    std::string::size_type i = 0; 
	  
    while ( i != s.size() ) { 
	// First occurance of idle character - low bound 
	while (i != s.size() &&  idle == s[i]) {
	    ++i; 
	}
	std::string::size_type j = i; 
	
	// Count until second occurance - high bound 
	while ( j != s.size() && idle != s[j]) {
	    ++j; 
	}

	if (i != j) { 
	    // get string part from low to high bound index 
	    ret.insert( s.substr(i, j - i) ); 
	    i = j; 
	} 
    }   

    return ret; 
};

FSObject::FSObject(string name, int atime, int ctime, int mtime) {
    this->name = name;
    this->atime = atime;
    this->ctime = ctime;
    this->mtime = mtime;
}
string FSObject::get_name() const {
    return this->name;
}
int FSObject::get_atime() {
    return this->atime;
}
int FSObject::get_ctime() {
    return this->ctime;
}
int FSObject::get_mtime() {
    return this->mtime;
}


static int get_tags_callback(void *callback_val, int argc, char** argv, char** colnames) {
    set<Tag>* tags =  (set<Tag>*) callback_val;

    tags->insert(Tag(argv[0], atoi(argv[1]?argv[1]:""), atoi(argv[2]?argv[2]:""), atoi(argv[3]?argv[3]:"")));
    
    return 0;
}

set<Tag> Tag::tags = set<Tag>();

void Tag::unserialize() {
    sqlite3* db = 0;
    char *zErrMsg = 0;
    int rc = 0;

    rc = sqlite3_open(DB_NAME, &db);

    if (rc) {
	string err = string(sqlite3_errmsg(db));
	sqlite3_close(db);
	throw DatabaseException(err.c_str());
    }

    char *err;
    
    Tag::tags.clear();
    
    string query = string("SELECT name, st_atime, st_ctime, st_mtime FROM tags");
    rc = sqlite3_exec(db, query.c_str(), get_tags_callback, &Tag::tags, &err);

    if (SQLITE_OK != rc) {
	string err = string(sqlite3_errmsg(db));
	sqlite3_close(db);
	throw DatabaseException(err.c_str());
    }

    sqlite3_close(db);
}
	

void Tag::get_tags_for_path(const char *path, set<Tag> & ret) {
    set<string> parts = split(path, '/');
    
    set<Tag>::iterator it;
    ret.clear();
    for(it = Tag::tags.begin(); it!=Tag::tags.end(); it++) {
	if (parts.count(it->get_name()) == 0) {
	    ret.insert(*it);
	}
    }
}

bool Tag::is_valid(const char *path) {
    set<string> parts = split(path, '/');
    
    set<Tag>::iterator it;
    for(it = Tag::tags.begin(); it!=Tag::tags.end(); it++) {
	if (parts.count(it->get_name()) >= 0) {
	    parts.erase(it->get_name());
	}
    }
    
    return parts.size() == 0;
}

bool Tag::operator<(const Tag& t) const {
    return strcmp(name.c_str(), t.name.c_str()) > 0;
}
